#ifndef __GLF_MATH_VEC3_H_INCLUDED__
#define __GLF_MATH_VEC3_H_INCLUDED__

#include <glf/math/functions.h>

namespace glf { 

template<typename T> struct vec3;
template<typename T> struct Vec3;

typedef vec3<float> vec3f;
typedef Vec3<float> Vec3f;

typedef vec3<int> vec3i;
typedef Vec3<int> Vec3i;

template<typename T> struct vec4;
template<typename T> struct Vec4;

typedef vec4<float> vec4f;
typedef Vec4<float> Vec4f;

typedef vec4<int> vec4i;
typedef Vec4<int> Vec4i;

template<typename T>
struct vec4
{
	T x,y,z,w;
};

template<typename T>
struct Vec4 : public vec4<T>
{
	Vec4();
	Vec4(T fx, T fy, T fz, T fw);
};

template<typename T>
Vec4<T>::Vec4() 
{
}

template<typename T>
Vec4<T>::Vec4(T fx, T fy, T fz, T fw) 
{
	 this->x = fx; 
	 this->y = fy; 
	 this->z = fz; 
	 this->w = fw;
}

template<typename T>
struct vec3
{
	T x;
	T y;
	T z;
};

template<typename T>
struct Vec3 : public vec3<T>
{
    Vec3();
    Vec3(const T* p);
    Vec3(T fx, T fy, T fz);
    Vec3(const vec3<T>& v);
    Vec3(const Vec3<T>& v);

    operator T*();
    operator const T*() const;

	operator Vec3*();
    operator const Vec3*() const;

    Vec3<T>& operator+=(const Vec3<T>& rhs);
    Vec3<T>& operator-=(const Vec3<T>& rhs);
    Vec3<T>& operator*=(T f);
    Vec3<T>& operator/=(T f);

    Vec3 operator+() const;
    Vec3 operator-() const;

    // The optimal implementation of binary operators depends on whether the return value is optimized.
    Vec3 operator+(const Vec3<T>& rhs) const;
    Vec3 operator-(const Vec3<T>& rhs) const;
    Vec3 operator*(T f) const;
    Vec3 operator/(T f) const;

    bool operator==(const Vec3<T>& rhs) const;
    bool operator!=(const Vec3<T>& rhs) const;

    T LenSq() const;
	inline T Len() const;
	T DistSq(const Vec3<T>& v2) const;
	T Dist(const Vec3<T>& v2) const;

	void Normalize();

	void Cross(vec3<T>& out, const vec3<T>& v) const;
	vec3<T> Cross(const vec3<T>& v) const;

	T Dot(const vec3<T>& v) const;
};

template<typename T>
Vec3<T>::Vec3() 
{
	
}

template<typename T>
Vec3<T>::Vec3(const T* p) 
{
	 this->x = p[0]; 
	 this->y = p[1]; 
	 this->z = p[2]; 
}

template<typename T>
Vec3<T>::Vec3(T fx, T fy, T fz) 
{
	 this->x = fx; 
	 this->y = fy; 
	 this->z = fz; 
}

template<typename T>
Vec3<T>::Vec3(const vec3<T>& v) 
{
	 this->x = v.x; 
	 this->y = v.y; 
	 this->z = v.z; 
}

template<typename T>
Vec3<T>::Vec3(const Vec3<T>& v) 
{
	 this->x = v.x; 
	 this->y = v.y; 
	 this->z = v.z; 
}

template<typename T>
Vec3<T>::operator T*() 
{
	 return &(this->x); 
}

template<typename T>
Vec3<T>::operator const T*() const 
{
	 return &(this->x); 
}

template<typename T>
Vec3<T>::operator Vec3<T>*() 
{
	 return (Vec3*)&(this->x);
}

template<typename T>
Vec3<T>::operator const Vec3<T>*() const 
{
	 return (const Vec3*)&(this->x);
}

template<typename T>
Vec3<T>& Vec3<T>::operator+=(const Vec3<T>& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	//VEC3Add(this, this, &rhs);
	return *this;
}

template<typename T>
Vec3<T>& Vec3<T>::operator-=(const Vec3<T>& rhs)
{
	this->x -= rhs.x;
	this->y -= rhs.y;
	this->z -= rhs.z;
	//VEC3Sub(this, this, &rhs);
	return *this;
}

template<typename T>
Vec3<T>& Vec3<T>::operator*=(T f)
{
	this->x *= f;
	this->y *= f;
	this->z *= f;
	//VEC3Scale(this, this, f);
	return *this;
}

template<typename T>
Vec3<T>& Vec3<T>::operator/=(T f) 
{
	 return operator*=(1.f / f); 
}

template<typename T>
Vec3<T> Vec3<T>::operator+() const 
{
	 return *this; 
}

template<typename T>
Vec3<T> Vec3<T>::operator-() const 
{
	 return Vec3(-this->x, -this->y, -this->z); 
}

// The optimal implementation of binary operators depends on whether the return value is optimized.
template<typename T>
Vec3<T> Vec3<T>::operator+(const Vec3<T>& rhs) const
{
	vec3<T> tmp = { this->x + rhs.x, this->y + rhs.y, this->z + rhs.z };
	//VEC3Add(&tmp, this, &rhs);
	return tmp;
}

template<typename T>
Vec3<T> Vec3<T>::operator-(const Vec3<T>& rhs) const
{
	vec3<T> tmp = { this->x - rhs.x, this->y - rhs.y, this->z - rhs.z };
	//VEC3Sub(&tmp, this, &rhs);
	return tmp;
}

template<typename T>
Vec3<T> Vec3<T>::operator*(T f) const
{
	vec3<T> tmp = { this->x * f, this->y * f, this->z * f};
	//VEC3Scale(&tmp, this, f);
	return tmp;
}

template<typename T>
Vec3<T> Vec3<T>::operator/(T f) const 
{
	 T r = 1.f / f; 
	 return operator*(r); 
}

template<typename T>
bool Vec3<T>::operator==(const Vec3<T>& rhs) const 
{
	 return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; 
}

template<typename T>
bool Vec3<T>::operator!=(const Vec3<T>& rhs) const 
{
	 return this->x != rhs.x || this->y != rhs.y || this->z != rhs.z; 
}

template<typename T>
T Vec3<T>::LenSq() const 
{
	 return this->x * this->x + this->y * this->y + this->z * this->z; 
}

template<typename T>
T Vec3<T>::Len() const 
{
	return Sqrt(LenSq());
}

template<typename T>
T Vec3<T>::DistSq(const Vec3<T>& v2) const 
{
	 return (this->x - v2.x)*(this->x - v2.x) + (this->y - v2.y)*(this->y - v2.y) + (this->z - v2.z)*(this->z - v2.z);
}

template<typename T>
T Vec3<T>::Dist(const Vec3<T>& v2) const 
{
	return Sqrt(DistSq(v2));
}


template<typename T>
void Vec3<T>::Normalize() 
{
	T invLen = T(1) / Len();
	this->x *= invLen;
	this->y *= invLen;
	this->z *= invLen;
}

template<typename T>
void Vec3<T>::Cross(vec3<T>& out, const vec3<T>& v) const
{
	out.x = (this->y * v.z) - ( this->z * v.y);
	out.y = (this->z * v.x) - ( this->x * v.z);
	out.z = (this->x * v.y) - ( this->y * v.x);
}

template<typename T>
vec3<T> Vec3<T>::Cross(const vec3<T>& v) const 
{
	vec3<T> out;
	Cross(out, v);
	return out;
}

template<typename T>
T Vec3<T>::Dot(const vec3<T>& v) const 
{
	return this->x * v.x + this->y * v.y + this->z * v.z;
}

} //namespace glf

#endif
